#ifndef SNAKECPP_H
#define SNAKECPP_H

#include <iostream>

using namespace std;

namespace Snake {
    const int BOARD_WIDTH = 10;
    const int BOARD_HEIGHT = 10;
    const int INITIAL_SNAKE_LENGTH = 2;

    // Direction enumeration
    enum Direction { UP, DOWN, LEFT, RIGHT };

    // Cell structure
    struct Cell {
        int x;
        int y;
        
        Cell(int x = 0, int y = 0) : x(x), y(y) {}
        bool operator==(const Cell& other) const {
            return x == other.x && y == other.y;
        }
    };

    // Game exceptions
    class SnakeException {
    public:
        SnakeException(const char* str){ std::strcpy(info,str); }
        const char* getInfo(){return this->info;}
    private:
        char info[256];
    };

    class Snake {
    public:
        Snake();
        ~Snake();
        Direction getDirection() const { return direction; }
        void setDirection(Direction dir);
        size_t getLength() const { return length; }
        const Cell& getHead() const;
        void move(bool grow = false);
        bool collidesWithCell(const Cell& cell) const;
        
        Snake(const Snake& s) = delete;
        Snake& operator=(const Snake& s) = delete;
        
    private:
        Cell** body;     // Array of Cell pointers
        size_t capacity;     // Capacity of the array
        size_t length;       // Current snake length
        Direction direction;
        
        void reallocate(size_t newCapacity);
    };

    class Food {
    public:
        Food() : cell(nullptr) {}
        ~Food() { delete cell; }
        
        const Cell& getCell() const { 
            if (!cell) throw SnakeException("Food not celled");
            return *cell; 
        }
        void setCell(int x, int y);
        
    private:
        Cell* cell;
    };

    class Board {
    public:
        Board();
        ~Board();
        void update();
        bool isGameOver() const { return gameOver; }
        const Snake& getSnake() const { return snake; }
        const Food& getFood() const { return food; }
        void changeSnakeDirection(Direction dir);
        void reset();
        
        Board(const Board& b);
        Board& operator=(const Board& b) = delete;
        
    private:
        Snake snake;
        Food food;
        bool gameOver;
        
        void spawnFood();
        bool isValidFoodCell(const Cell& cell) const;
    };

    class GameController {
    public:
        GameController();
        ~GameController();
        void processInput(char input);
        void update();
        void render(ostream& os = cout) const;
        
        GameController(const GameController& gc) = delete;
        GameController& operator=(const GameController& gc) = delete;
        
    private:
        Board* board;
    };

    ostream& operator<<(ostream& f, const Cell& p);
    ostream& operator<<(ostream& f, const Board& b);
}

#endif // SNAKECPP_H